<?php

declare(strict_types=1);

namespace Zms\Validate\Rule;

use Zms\Unit\Is;
use Zms\Unit\Number;

/**
 * 整数验证器
 *
 * 该类用于检查给定值是否为有效的整数，并且可以设置最大值、最小值限制。它继承自 RuleAbstract，
 * 并提供了设置错误消息、最大值、最小值以及执行具体验证逻辑的方法。
 */
class IntValidate extends RuleAbstract
{
    /**
     * 默认错误消息
     *
     * 当验证失败时显示的默认错误消息。可以包含占位符 `{label}`，将在实际使用时替换为字段标签。
     *
     * @var string
     */
    protected $_error = '{label}必须是整数';

    /**
     * 数字最小值
     *
     * 可以设置一个最小值，如果输入值小于这个值，则认为验证失败。
     *
     * @var float|int|null
     */
    protected $_min = null;

    /**
     * 最小值错误提示信息
     *
     * 当输入值小于设定的最小值时显示的错误消息。可以包含占位符 `{label}` 和 `{min}`。
     *
     * @var string
     */
    protected $_minError = "{label}不能小于{min}";

    /**
     * 数字最大值
     *
     * 可以设置一个最大值，如果输入值大于这个值，则认为验证失败。
     *
     * @var int|float|null
     */
    protected $_max = null;

    /**
     * 最大值错误提示信息
     *
     * 当输入值大于设定的最大值时显示的错误消息。可以包含占位符 `{label}` 和 `{max}`。
     *
     * @var string
     */
    protected $_maxError = "{label}不能大于{max}";

    /**
     * 设置数字最大值
     *
     * 允许调用者动态地设置或修改最大值。
     *
     * @param int|float $max 新的最大值
     * @return $this 返回当前对象实例，便于链式调用
     */
    public function setMax($max): IntValidate
    {
        $this->_max = $max;
        return $this;
    }

    /**
     * 设置最大值错误提示信息
     *
     * 允许调用者动态地设置或修改最大值错误提示信息。
     *
     * @param string $maxError 新的最大值错误提示信息
     * @return $this 返回当前对象实例，便于链式调用
     */
    public function setMaxError(string $maxError): IntValidate
    {
        $this->_maxError = $maxError;
        return $this;
    }

    /**
     * 设置数字最小值
     *
     * 允许调用者动态地设置或修改最小值。
     *
     * @param int|float $min 新的最小值
     * @return $this 返回当前对象实例，便于链式调用
     */
    public function setMin($min): IntValidate
    {
        $this->_min = $min;
        return $this;
    }

    /**
     * 设置最小值错误提示信息
     *
     * 允许调用者动态地设置或修改最小值错误提示信息。
     *
     * @param string $minError 新的最小值错误提示信息
     * @return $this 返回当前对象实例，便于链式调用
     */
    public function setMinError(string $minError): IntValidate
    {
        $this->_minError = $minError;
        return $this;
    }

    /**
     * 开始验证
     *
     * 验证输入值是否为有效的整数，并检查是否满足最小值和最大值限制。
     * 如果输入值不是数值类型或者不满足条件，则添加相应的错误信息并返回 false。
     *
     * @param mixed $value 待验证的值
     * @param string $label 字段标签，用于错误提示信息中
     * @return bool 验证结果：成功为 true，失败为 false
     */
    public function validate($value, string $label = ''): bool
    {
        if (is_numeric($value)) {
            // 如果允许跳过空值并且值为空，则直接通过验证
            if (Is::empty($value) && $this->_noSkipping === true) {
                $this->_value = $value;
                return true;
            }
            // 格式化数值
            $num = Number::format($value);
            // 检查最小值限制
            if (is_int($this->_min) || is_float($this->_min)) {
                if ($num < $this->_min) {
                    $this->addError($this->_minError, $label);
                }
            }
            // 检查最大值限制
            if (is_int($this->_max) || is_float($this->_max)) {
                if ($num > $this->_max) {
                    $this->addError($this->_maxError, $label);
                }
            }
            // 检查是否为整数
            if (Number::precision($num) > 0) {
                $this->addError("{label}只能是整数", $label);
            }
            // 如果有错误信息则返回 false
            if ($this->isFail()) {
                return false;
            }
            // 记录值并返回 true 表示验证通过
            $this->_value = $value;
            return true;
        } elseif (is_string($value) && Is::empty($value)) {
            // 如果输入值为空字符串且允许跳过，则直接通过验证
            if ($this->_noSkipping) {
                $this->_value = $value;
                return true;
            }
        }
        // 添加默认错误信息并返回 false
        $this->addError($this->_error, $label);
        return false;
    }
}